using System;
using System.Globalization;
using System.Xml;
using Legacy.Core.Api;
using Legacy.Core.SaveGameManagement;

namespace SaveGameConverter
{
    internal sealed class Xml2Lsg
    {
        public static void Convert(string sourceFile, string targetFile)
        {
            Xml2Lsg converter = new Xml2Lsg(sourceFile, targetFile);
            converter.Convert();
        }

        private readonly string _sourceFile;
        private readonly string _targetFile;

        private Xml2Lsg(string sourceFile, string targetFile)
        {
            if (sourceFile == null)
                throw new ArgumentNullException("sourceFile");
            if (targetFile == null)
                throw new ArgumentNullException("targetFile");

            _sourceFile = sourceFile;
            _targetFile = targetFile;
        }

        public void Convert()
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(_sourceFile);

            SaveGame sg = new SaveGame();

            XmlElement root = doc["SavedGame"];
            if (root == null)
                throw new Exception("SaveGame node is not found.");

            foreach (XmlElement child in root.ChildNodes)
            {
                if (child.Name == "SaveGameMeta")
                    Deserialize(child, sg);
                else if (child.Name == "SaveGameData")
                    sg.Add(Deserialize(child));
            }

            sg.Write(_targetFile);
        }

        private void Deserialize(XmlElement meta, SaveGame sg)
        {
            Int64 timeInTicks = Int64.Parse(meta.Attributes["Int64_TimeInTicks"].Value, CultureInfo.InvariantCulture);
            Int64 playTimeInTicks = Int64.Parse(meta.Attributes["Int64_PlayTimeInTicks"].Value, CultureInfo.InvariantCulture);
            Int32 saveNumber = Int32.Parse(meta.Attributes["Int32_SaveNumber"].Value, CultureInfo.InvariantCulture);
            Int32 saveGameType = Int32.Parse(meta.Attributes["Int32_SaveGameType"].Value, CultureInfo.InvariantCulture);

            SaveGameMeta sgm = new SaveGameMeta(new DateTime(timeInTicks), new TimeSpan(playTimeInTicks), saveNumber, (ESaveGameType)saveGameType);
            sg.SetPrivateFieldValue("m_metaData", sgm);
            
            // See SaveGame.WriteMetaData
            LegacyLogic.Instance.WorldManager.HighestSaveGameNumber = saveNumber;
            LegacyLogic.Instance.WorldManager.CurrentSaveGameType = (ESaveGameType)saveGameType;
        }

        private SaveGameData Deserialize(XmlElement node)
        {
            SaveGameData result = new SaveGameData(node.Attributes["id"].Value);

            foreach (XmlElement element in node.ChildNodes)
                foreach (XmlElement entry in element.ChildNodes)
                {
                    try
                    {
                        if (element.Name == Helper.Object)
                        {
                            SaveGameData data = Deserialize(entry);
                            result.Set(data.ID, Deserialize(entry));
                            continue;
                        }

                        string name = entry.Attributes["name"].Value;
                        string value = entry.Attributes["value"].Value;

                        if (element.Name == Helper.Boolean)
                            result.Set(name, Boolean.Parse(value));
                        else if (element.Name == Helper.Byte)
                            result.Set(name, Byte.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.Int16)
                            result.Set(name, Int16.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.UInt16)
                            result.Set(name, UInt16.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.Int32)
                            result.Set(name, Int32.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.UInt32)
                            result.Set(name, UInt32.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.Int64)
                            result.Set(name, Int64.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.UInt64)
                            result.Set(name, UInt64.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.Single)
                            result.Set(name, Single.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.Double)
                            result.Set(name, Double.Parse(value, CultureInfo.InvariantCulture));
                        else if (element.Name == Helper.String)
                            result.Set(name, value);
                        else
                            throw new Exception(String.Format("Unknown element name: {0}", element.Name));
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(String.Format("Failed to process node: {0}", entry.InnerXml), ex);
                    }
                }

            return result;
        }
    }
}